/*
*	Developed by Leonardo Chatain and Pedro Dusso, at Universidade Federal do Rio Grande do Sul, Brasil, 2009/1
*	Source available at glpacman.codeplex.com
*/

#include "Maze.h"

Maze::Maze(void){
}

Maze::~Maze(void) {
	delete maze;
}

// Loads a maze from a text file.
// Uses the conventions found in Definitions.h
// The first two lines of the text file are the number of lines and the number of columns, respectively
// Follows the maze, using the maze convention
// Follows additional information, such as textures
void Maze::openMaze(const char* path) {
	FILE *mazeFile;
	if ((mazeFile = fopen(path, "r")) == NULL) {
		cout << "File not found" << endl;
		system("pause");
		exit(1);
	}

	//Reads, in order, the number of lines and the number of columns

	fscanf(mazeFile, "%d", &lines);
	fscanf(mazeFile, "%d", &columns);
	fscanf(mazeFile, "%d", &nPhantoms);

	// creates the maze
	maze = new char*[lines];
	for(int i = 0; i < lines; i++)
		maze[i] = new char[columns];

	// creates the vector of phantoms positions
	phantomStart = new Point2D[nPhantoms];

	// Is the phantom being detected in the maze
	int currentPhantom = 0;

	// Reads the file and fills the memory maze
	char *lineBuffer = new char[columns+1];
	for(int l = 0; l < lines; l++) {
		fscanf(mazeFile, "%s", lineBuffer);
		// Maze consistency verification
		for (int r = 0; r < columns; r++) {
			if (lineBuffer[r] == PAC_START) {
				pacStart.setXY(r*CELL_SIZE + CELL_SIZE/2, l*CELL_SIZE + CELL_SIZE/2);
			}
			if (lineBuffer[r] == PILL || lineBuffer[r] == SUPER_PILL)
				nPills++;
			if (lineBuffer[r] == PHANTOM_START) {
				phantomStart[currentPhantom].setXY(r*CELL_SIZE + CELL_SIZE/2, l*CELL_SIZE + CELL_SIZE/2);
				currentPhantom++;
			}
			if (lineBuffer[r] == WALL ||
				lineBuffer[r] == EMPTY ||
				lineBuffer[r] == PILL ||
				lineBuffer[r] == PAC_START ||
				lineBuffer[r] == PHANTOM_START ||
				lineBuffer[r] == TELEPORT ||
				lineBuffer[r] == SUPER_PILL)
				maze[l][r] = lineBuffer[r];
			else {
				cout << "Invalid Maze" << endl;
				system("pause");
				exit(1);
			}
		}
	}
	if (currentPhantom != nPhantoms) {
		cout << "Error in nPhantoms consistency" << endl;
		cout << "nPhantoms: " << nPhantoms << endl;
		cout << "currentPhantom: " <<  currentPhantom << endl;
		cout << "npills: " << nPills << endl;
		system("pause");
		exit(1);
	}
	fclose(mazeFile);
}

void Maze::drawFloor() {
	int width = CELL_SIZE * columns;
	int height = CELL_SIZE * lines;
	glColor3f(FLOOR_COLOR);
	glBegin( GL_POLYGON );
	glVertex3f(0,0,0);
	glVertex3f(width,0,0);
	glVertex3f(width,0,height);
	glVertex3f(0,0,height);
	glEnd();     

}

void Maze::drawRoof() {
	glColor3f(0.0,0.0,1.0);
	glBegin( GL_POLYGON );
	glVertex3f(0,0,0);
	glVertex3f(columns,0,0);
	glVertex3f(columns,0,lines);
	glVertex3f(0,0,lines);
	glEnd();  

}
void Maze::drawSkyBox() {
	glFrontFace(GL_CW);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, planet1Tex.TextureID);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

	glBegin(GL_POLYGON);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1000, -300, -1000);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1000, 1000, -1000);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(columns*CELL_SIZE+1000, -300, -1000);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(columns*CELL_SIZE+1000, 1000, -1000);
	glEnd();
	
	glBindTexture(GL_TEXTURE_2D, planet2Tex.TextureID);
	glBegin(GL_POLYGON);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1000, -300, -1000);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1000, 1000, -1000);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1000, -300, lines*CELL_SIZE+1000);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1000, 1000, lines*CELL_SIZE+1000);
	glEnd();
	
	glBindTexture(GL_TEXTURE_2D, planet3Tex.TextureID);
	glBegin(GL_POLYGON);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1000, -300, lines*CELL_SIZE+1000);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1000, 1000, lines*CELL_SIZE+1000);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(columns*CELL_SIZE+1000, -300, lines*CELL_SIZE+1000);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(columns*CELL_SIZE+1000, 1000, lines*CELL_SIZE+1000);
	glEnd();
	
	glBindTexture(GL_TEXTURE_2D, planet4Tex.TextureID);
	glBegin(GL_POLYGON);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(columns*CELL_SIZE+1000, -300, -1000);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(columns*CELL_SIZE+1000, 1000,-1000);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(columns*CELL_SIZE+1000, -300, lines*CELL_SIZE+1000);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(columns*CELL_SIZE+1000, 1000, lines*CELL_SIZE+1000);
	glEnd();

	glDisable(GL_TEXTURE_2D);
}
void Maze::drawWall() {
	const int faces[6][6] = {
		{0, 1, 5, 4},     // face 0 voltada para z positivo
		{1, 2, 6, 5},     // face 1 voltada para x positivo
		{2, 3, 7, 6},     // face 2 voltada para z negativo
		{3, 0, 4, 7},     // face 3 voltada para x negativo
		{4, 5, 6, 7},     // topo
		{3, 2, 1, 0}      // base
	};

	const PontoXYZ vertexes[8] = {
		{0.0, 0.0, 0.0},	// 0
		{CELL_SIZE, 0.0, 0.0},	//1
		{CELL_SIZE, 0.0, CELL_SIZE},	//2
		{0.0, 0.0, CELL_SIZE},	//3
		
		{0.0, WALL_HEIGHT, 0.0},	//4
		{CELL_SIZE, WALL_HEIGHT, 0.0},	//5
		{CELL_SIZE, WALL_HEIGHT, CELL_SIZE},	//6
		{0.0, WALL_HEIGHT, CELL_SIZE}	//7
	};

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, wallTex.TextureID);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	for(int i = 0; i < 6; i++) {
		glColor3f(WALL_COLOR);
		glBegin( GL_POLYGON );
			switch (i) { 
				case 0: glNormal3f(0.0,0.0,1.0); break;
				case 1: glNormal3f(1.0,0.0,0.0); break;
				case 2: glNormal3f(0.0,1.0,-1.0); break;
				case 3: glNormal3f(-1.0,0.0,0.0); break;
				case 4: glNormal3f(0.0,1.0,0.0); break;
				case 5: glNormal3f(0.0,-1.0,0.0); break;
			}			
			glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertexes[ faces[i][0] ]);
			glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertexes[ faces[i][1] ]);
			glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertexes[ faces[i][2] ]);
			glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertexes[ faces[i][3] ]);		
		
		glEnd();
	}
	glDisable(GL_TEXTURE_2D);
}

void Maze::drawPill() {
	glPushMatrix();
	glTranslatef(CELL_SIZE / 2, CELL_SIZE / 2, CELL_SIZE / 2);
	glColor3f(YELLOW);
	glutSolidSphere(PILL_RADIUS, CHARACTER_SLICES, CHARACTER_STACKS);
	glPopMatrix();
}
void Maze::drawSuperPill() {
	glPushMatrix();
	glTranslatef(CELL_SIZE / 2, CELL_SIZE / 2, CELL_SIZE / 2);
	glColor3f(WHITE);
	glutSolidSphere(PILL_RADIUS*3, CHARACTER_SLICES, CHARACTER_STACKS);
	glPopMatrix();
}
void Maze::draw() {
	static bool firstTime = true;
	if (firstTime) {
		texLoader.LoadTextureFromDisk("anvil1.bmp", &wallTex);
		texLoader.LoadTextureFromDisk("planet1.jpg", &planet1Tex);
		texLoader.LoadTextureFromDisk("planet2.jpg", &planet2Tex);
		texLoader.LoadTextureFromDisk("planet3.jpg", &planet3Tex);
		texLoader.LoadTextureFromDisk("planet4.jpg", &planet4Tex);
		firstTime = false;
	}

	drawSkyBox();

	glPushMatrix();
	int lastLine = 0, lastColumn = 0;
	for (int l = 0; l < lines; l++) {
		for (int r = 0; r < columns; r++) {
			if(maze[l][r] == WALL) {
				//Translates the wall cell based on the last cell drawed
				glTranslatef((r - lastColumn) * CELL_SIZE, 0, (l-lastLine) * CELL_SIZE);
				drawWall();
				glPushMatrix();
				glTranslatef(CELL_SIZE/2,CELL_SIZE/2, CELL_SIZE/2);
				
				glPopMatrix();
				lastLine = l;
				lastColumn = r;
			}
			else if (maze[l][r] == PILL) {
				glTranslatef((r - lastColumn) * CELL_SIZE, 0, (l-lastLine) * CELL_SIZE);
				drawPill();
				lastLine = l;
				lastColumn = r;
			}
			else if (maze[l][r] == SUPER_PILL) {
				glTranslatef((r - lastColumn) * CELL_SIZE, 0, (l-lastLine) * CELL_SIZE);
				drawSuperPill();
				lastLine = l;
				lastColumn = r;
			}
		}
	}
	glPopMatrix();
	drawFloor();
}